﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Threading;
using System.Collections;
using System.Diagnostics;
using System.Reflection;
using DTO;
using DAL;

namespace BLL
{
    public class GA
    {
        private double _mutationRate = 0f;
        private double _crossoverRate = 0;
        private int _populationSize = 0;
        private int _generationSize = 0;
        private double _totalFitness = 0;       
        private int _numEvaluate;
        private int _seed = 0;
        private int _touranmentSize = 0;        
        private int _numCity;

        private int _percentVNSApplyLocalsearch;    

        private Random ran;
      
        private List<PathTour> currentPathPop;
        private List<PathTour> nextPathPop;

        private bool _elitism = false;      
      
        public int PopulationSize
        {
            get { return _populationSize; }
            set { _populationSize = value; }
        }      
        public int Seed
        {
            get { return _seed; }
            set { _seed = value; }
        }
        public bool Elitism
        {
            get { return _elitism; }
            set { _elitism = value; }
        }  
        public int TouranmentSize
        {
            get { return _touranmentSize; }
            set { _touranmentSize = value; }
        }

        //GA: giai_thuat(0); ten_file_du_lieu(1); so_lan_danh_gia(2); seed(3);
        public GA(int numCity, int numEvaluate, int seed)
        {
            _numCity = numCity;          
            _seed = seed;          
            _numEvaluate = numEvaluate;
            ran = new Random(_seed);
        }
                        
         //GA: giai_thuat(0); ten_file_du_lieu(1); so_lan_danh_gia(2); ti_le_lai_ghep(3); kich_thuoc_quan_the(4); tourament_size(5); elitis(6); seed(7);
        public GA(int numCity, int numEvaluate, float crossoverRate, int popSize, int touramentSize, bool elitism, int seed)
        {
            _numCity = numCity;
            _mutationRate = (double)1 / (double)numCity;
            _crossoverRate = crossoverRate;
            _populationSize = popSize;
            _generationSize = (int)((numEvaluate / popSize));
            _touranmentSize = touramentSize;
            _seed = seed;
            _elitism = elitism;
            _numEvaluate = numEvaluate;
        }

        //GA use VNS Local search
        //GA: giai_thuat(0); ten_file_du_lieu(1); so_lan_danh_gia(2); ti_le_lai_ghep(3); kich_thuoc_quan_the(4); tourament_size(5); elitis(6); seed(7);percentApplyVNSLocalsearch(8)
        public GA(int numCity, int numEvaluate, float crossoverRate, int popSize, int touramentSize, bool elitism, int seed, int percentApplyVNSLocalsearch)
        {
            _numCity = numCity;
            _mutationRate = (double)1 / (double)numCity;
            _crossoverRate = crossoverRate;
            _populationSize = popSize;
            //_generationSize = (int)((numEvaluate / popSize));
            _touranmentSize = touramentSize;
            _seed = seed;
            _elitism = elitism;
            _numEvaluate = numEvaluate;
            _percentVNSApplyLocalsearch = percentApplyVNSLocalsearch;
            _generationSize = (int)(numEvaluate / (popSize * (1 + percentApplyVNSLocalsearch / 100.0)));
        }

        #region GA with MSCX for path representation

            private bool compareTour(PathTour tour1, PathTour tour2, int numCity)
            {
                bool ok = true;
                for (int i = 0; i < numCity; i++)
                {
                    if (tour1.OrderCity[i] != tour2.OrderCity[i])
                    {
                        ok = false;
                        break;
                    }
                }
                return ok;
            }

            private void initPathRepresetations()
            {
                currentPathPop = new List<PathTour>();
                nextPathPop = new List<PathTour>();
                ran = new Random(_seed);
            }

            public void createRandomPopulation(int numCity, ref PathTour bestTour, CityObject[] coor, Random ran)
            {
                currentPathPop.Clear();
                for (int i = 0; i < _populationSize; i++)
                {
                    PathTour ch = new PathTour(numCity);
                    InitChromo initChromo = new InitChromo();
                    initChromo.createPathTour(ref ch, numCity, ran);
                    Evaluate eval = new Evaluate();
                    eval.calculateFitness(ref ch, ref bestTour, numCity, coor);
                    currentPathPop.Add(ch);
                }
            }

            public void createNextPopulationMSCX(ref List<PathTour> curPop, List<PathTour> nextPop, ref PathTour bestTour, int numCity, int popSize, 
                int touranmentSize, double mutationRat, double crossRate, CityObject[] coor, Random ran)
            {
                int index = 0;
                int pidx1, pidx2;
                PathTour parent1 = new PathTour(numCity);
                PathTour parent2 = new PathTour(numCity);
                PathTour child1 = new PathTour(numCity);
                PathTour child2 = new PathTour(numCity);
                nextPop.Clear();
                Selections sl = new Selections();
                for (int i = 0; i < popSize; i += 1)
                {
                    pidx1 = sl.touranmentSelection(ran, curPop, popSize, touranmentSize);
                    pidx2 = sl.touranmentSelection(ran, curPop, popSize, touranmentSize);
                    parent1 = curPop[pidx1];
                    parent2 = curPop[pidx2];
                    if ((float)(ran.NextDouble()) < _crossoverRate)
                    {
                        Crossovers cross = new Crossovers();
                        cross.MSCX(parent1, parent2, out child1, numCity, coor, ran);
                        Mutation mutation = new Mutation();
                        mutation.inversionMutation(ref child1, numCity, mutationRat, ran);
                        //child2.inversionMutation(ran);                                    
                        Evaluate eval = new Evaluate();
                        eval.calculateFitness(ref child1, ref bestTour, numCity, coor);
                        nextPop.Add(child1);
                        //child2.calDistance = new Chromosome.CaculateDistance(InOutData.calculateDistance);
                        //child2.calculateFitness(_maxCost);
                        //nextPop.Add(child2);
                    }
                    else
                    {
                        nextPop.Add(parent1);
                        //nextPop.Add(parent2);
                    }
                }
                curPop.Clear();
                for (int i = 0; i < _populationSize; i++)
                {
                    curPop.Add(nextPop[i]);
                    if ((nextPop[i]).Fitness < (nextPop[index]).Fitness)
                    {
                        index = i;
                    }
                }
                if (_elitism)
                {
                    if (!compareTour(bestTour, curPop[index], numCity))
                    {
                        curPop.RemoveAt(0);
                        curPop.Add(bestTour);
                    }
                }
            }

            public void GA_MSCX(ref PathTour bestTour, CityObject[] coor, string outFile)
            {
                initPathRepresetations();
                createRandomPopulation(_numCity, ref bestTour, coor, ran);
                Evaluate eval = new Evaluate();
                _totalFitness = eval.totalFitnessPopulation(currentPathPop, _populationSize);
                ReadWriteFile writeFile = new ReadWriteFile();
                if (File.Exists(outFile))
                {
                    File.Delete(outFile);
                }
                for (int i = 0; i < _generationSize; i++)
                {
                    createNextPopulationMSCX(ref currentPathPop, nextPathPop, ref bestTour, _numCity, _populationSize, _touranmentSize,
                        _mutationRate, _crossoverRate, coor, ran);
                    _totalFitness = eval.totalFitnessPopulation(currentPathPop, _populationSize);
                    writeFile.writeBestFitnessInPop(outFile, currentPathPop, _totalFitness, i + 1, bestTour);
                }
            }
                     
            //GA use only SRD Crossover
            public void createNextPopulation_SRD(ref List<PathTour> curPop, List<PathTour> nextPop, ref PathTour bestTour, int numCity, int num_random,
                int popSize, int touranmentSize, double mutationRat, double crossRate, CityObject[] coor, Random ran)
            {
                int index = 0;
                int pidx1, pidx2;
                PathTour parent1 = new PathTour(numCity);
                PathTour parent2 = new PathTour(numCity);
                PathTour child1 = new PathTour(numCity);
                PathTour child2 = new PathTour(numCity);
                nextPop.Clear();
                Selections sl = new Selections();
                for (int i = 0; i < popSize; i += 2)
                {
                    pidx1 = sl.touranmentSelection(ran, curPop, popSize, touranmentSize);
                    pidx2 = sl.touranmentSelection(ran, curPop, popSize, touranmentSize);
                    parent1 = curPop[pidx1];
                    parent2 = curPop[pidx2];
                    if ((float)(ran.NextDouble()) < _crossoverRate)
                    {
                        Crossovers cross = new Crossovers();
                        cross.CRD(num_random, parent1, parent2, out child1, out child2, numCity, coor, ran);
                        Mutation mutation = new Mutation();
                        mutation.inversionMutation(ref child1, numCity, mutationRat, ran);
                        mutation.inversionMutation(ref child2, numCity, mutationRat, ran);                           
                        Evaluate eval = new Evaluate();
                        eval.calculateFitness(ref child1, ref bestTour, numCity, coor);
                        nextPop.Add(child1);
                        eval.calculateFitness(ref child2, ref bestTour, numCity, coor);
                        nextPop.Add(child2);
                    }
                    else
                    {
                        nextPop.Add(parent1);
                        nextPop.Add(parent2);
                    }
                }
                curPop.Clear();
                for (int i = 0; i < _populationSize; i++)
                {
                    curPop.Add(nextPop[i]);
                    if ((nextPop[i]).Fitness < (nextPop[index]).Fitness)
                    {
                        index = i;
                    }
                }
                if (_elitism)
                {
                    if (!compareTour(bestTour, curPop[index], numCity))
                    {
                        curPop.RemoveAt(0);
                        curPop.Add(bestTour);
                    }
                }
            }

            //GA: giai_thuat(0); ten_file_du_lieu(1); so_lan_danh_gia(2); ti_le_lai_ghep(3); kich_thuoc_quan_the(4); tourament_size(5); elitis(6); seed(7); num_random_cities(8)
            public void GA_SRD(int num_random, ref PathTour bestTour, CityObject[] coor, string outFile)
            {
                initPathRepresetations();
                createRandomPopulation(_numCity, ref bestTour, coor, ran);
                Evaluate eval = new Evaluate();
                _totalFitness = eval.totalFitnessPopulation(currentPathPop, _populationSize);
                ReadWriteFile writeFile = new ReadWriteFile();
                if (File.Exists(outFile))
                {
                    File.Delete(outFile);
                }
                for (int i = 0; i < _generationSize; i++)
                {
                    createNextPopulation_SRD(ref currentPathPop, nextPathPop, ref bestTour, _numCity, num_random, _populationSize, _touranmentSize,
                        _mutationRate, _crossoverRate, coor, ran);
                    _totalFitness = eval.totalFitnessPopulation(currentPathPop, _populationSize);
                    writeFile.writeBestFitnessInPop(outFile, currentPathPop, _totalFitness, i + 1, bestTour);
                }
            }
           
            
            //GA: use OX Crossover
            public void createNextPopulation_OX(ref List<PathTour> curPop, List<PathTour> nextPop, ref PathTour bestTour, int numCity, int popSize,
                   int touranmentSize, double mutationRate, double crossRate, CityObject[] coor, Random ran)
            {
                int index = 0;
                int pidx1, pidx2;
                PathTour parent1 = new PathTour(numCity);
                PathTour parent2 = new PathTour(numCity);
                PathTour child1 = new PathTour(numCity);
                PathTour child2 = new PathTour(numCity);
                nextPop.Clear();
                Selections sl = new Selections();
                for (int i = 0; i < popSize; i += 2)
                {
                    pidx1 = sl.touranmentSelection(ran, curPop, popSize, touranmentSize);
                    pidx2 = sl.touranmentSelection(ran, curPop, popSize, touranmentSize);
                    parent1 = curPop[pidx1];
                    parent2 = curPop[pidx2];
                    if ((float)(ran.NextDouble()) < _crossoverRate)
                    {
                        Crossovers cross = new Crossovers();
                        cross.orderCrossover(parent1, parent2, out child1, out child2, ran);
                        Mutation mutation = new Mutation();
                        mutation.inversionMutation(ref child1, numCity, mutationRate, ran);
                        mutation.inversionMutation(ref child2, numCity, mutationRate, ran);
                                 
                        Evaluate eval = new Evaluate();
                        eval.calculateFitness(ref child1, ref bestTour, numCity, coor);
                        nextPop.Add(child1);
                        eval.calculateFitness(ref child2, ref bestTour, numCity, coor);
                        nextPop.Add(child2);                    
                    }
                    else
                    {
                        nextPop.Add(parent1);
                        nextPop.Add(parent2);
                    }
                }
                curPop.Clear();
                for (int i = 0; i < _populationSize; i++)
                {
                    curPop.Add(nextPop[i]);
                    if ((nextPop[i]).Fitness < (nextPop[index]).Fitness)
                    {
                        index = i;
                    }
                }
                if (_elitism)
                {
                    if (!compareTour(bestTour, curPop[index], numCity))
                    {
                        curPop.RemoveAt(0);
                        curPop.Add(bestTour);
                    }
                }
            }

            public void GA_OX(ref PathTour bestTour, CityObject[] coor, string outFile)
            {
                initPathRepresetations();
                createRandomPopulation(_numCity, ref bestTour, coor, ran);
                Evaluate eval = new Evaluate();
                _totalFitness = eval.totalFitnessPopulation(currentPathPop, _populationSize);
                ReadWriteFile writeFile = new ReadWriteFile();
                if (File.Exists(outFile))
                {
                    File.Delete(outFile);
                }
                for (int i = 0; i < _generationSize; i++)
                {
                    createNextPopulation_OX(ref currentPathPop, nextPathPop, ref bestTour, _numCity, _populationSize, _touranmentSize,
                        _mutationRate, _crossoverRate, coor, ran);
                    _totalFitness = eval.totalFitnessPopulation(currentPathPop, _populationSize);
                    writeFile.writeBestFitnessInPop(outFile, currentPathPop, _totalFitness, i + 1, bestTour);
                }
            }

        #endregion

        #region GA with VNS --> evaluate
            public void createRandomPopulation(int numCity, ref PathTour bestTour, CityObject[] coor, ref int countEvaluate, Random ran)
            {
                currentPathPop.Clear();
                for (int i = 0; i < _populationSize; i++)
                {
                    PathTour ch = new PathTour(numCity);
                    InitChromo initChromo = new InitChromo();
                    initChromo.createPathTour(ref ch, numCity, ran);
                    Evaluate eval = new Evaluate();
                    eval.calculateFitness(ref ch, ref bestTour, numCity, coor);
                    countEvaluate++;
                    currentPathPop.Add(ch);
                }
            }
        
            #region 2-Opt Neighborhood

            //GA use OX crossover and VNS local search
               public void applyVNSForPop(ref List<PathTour> curPop, int numTourApplyLocalSearch, int numCity, CityObject[] citiesCoor, ref int countEvaluate,
                int totalEvaluate, ref PathTour btour, Random ran)
            {
                int pos = 0;
                VariableNeighborHoodSearch vns = new VariableNeighborHoodSearch();
                if (numTourApplyLocalSearch == 100)
                {
                    List<PathTour> lstTemp = new List<PathTour>();
                    for (int jj = 0; jj < _populationSize; jj++)
                    {
                        if (countEvaluate > totalEvaluate)
                        {
                            break;
                        }
                        PathTour temp = new PathTour(numCity);
                        temp = (PathTour)(curPop[jj]).Clone();
                        temp = vns.VNS_2Opt(temp, citiesCoor, ref btour, ref countEvaluate, totalEvaluate, ran);
                        lstTemp.Add(temp);                      
                    }
                    curPop.Clear();
                    for (int i = 0; i < lstTemp.Count; i++)
                    {
                        curPop.Add(lstTemp[i]);
                    }
                }
                else
                {
                    for (int jj = 0; jj < numTourApplyLocalSearch; jj++)
                    {
                        if (countEvaluate > totalEvaluate)
                        {
                            break;
                        }
                        PathTour temp = new PathTour(numCity);
                        pos = ran.Next(_populationSize);
                        temp = (PathTour)(curPop[pos]).Clone();
                        curPop.RemoveAt(pos);
                        temp = vns.VNS_2Opt(temp, citiesCoor, ref btour, ref countEvaluate, totalEvaluate, ran);
                        curPop.Add(temp);                       
                    }
                }

            }

               public void createNextPopulation_OX_VNS(ref List<PathTour> curPop, List<PathTour> nextPop, ref PathTour bestTour, int numCity, int popSize,
                int touranmentSize, double mutationRate, double crossRate, int percentApplyLocalsearch, CityObject[] coor, ref int countEvaluate,
                int totalEvaluate, Random ran)
            {
                int index = 0;
                int pidx1, pidx2;
                PathTour parent1 = new PathTour(numCity);
                PathTour parent2 = new PathTour(numCity);
                PathTour child1 = new PathTour(numCity);
                PathTour child2 = new PathTour(numCity);
                nextPop.Clear();
                Selections sl = new Selections();
                for (int i = 0; i < popSize; i += 2)
                {
                    pidx1 = sl.touranmentSelection(ran, curPop, popSize, touranmentSize);
                    pidx2 = sl.touranmentSelection(ran, curPop, popSize, touranmentSize);
                    parent1 = curPop[pidx1];
                    parent2 = curPop[pidx2];
                    if ((float)(ran.NextDouble()) < _crossoverRate)
                    {
                        Crossovers cross = new Crossovers();
                        cross.orderCrossover(parent1, parent2, out child1, out child2, ran);
                        Mutation mutation = new Mutation();
                        mutation.inversionMutation(ref child1, numCity, mutationRate, ran);
                        mutation.inversionMutation(ref child2, numCity, mutationRate, ran);

                        Evaluate eval = new Evaluate();
                        eval.calculateFitness(ref child1, ref bestTour, numCity, coor);
                        nextPop.Add(child1);
                        eval.calculateFitness(ref child2, ref bestTour, numCity, coor);
                        nextPop.Add(child2);
                        countEvaluate = countEvaluate + 2;
                        if (countEvaluate > totalEvaluate)
                        {
                            break;
                        }
                    }
                    else
                    {
                        nextPop.Add(parent1);
                        nextPop.Add(parent2);
                    }
                }
                applyVNSForPop(ref nextPop, (int)(percentApplyLocalsearch * _populationSize / 100), numCity, coor, ref countEvaluate, totalEvaluate, ref bestTour, ran);

                if (countEvaluate > totalEvaluate)
                {
                    return;
                }

                curPop.Clear();
                for (int i = 0; i < _populationSize; i++)
                {
                    curPop.Add(nextPop[i]);
                    if ((nextPop[i]).Fitness < (nextPop[index]).Fitness)
                    {
                        index = i;
                    }
                }
                if (_elitism)
                {
                    if (!compareTour(bestTour, curPop[index], numCity))
                    {
                        curPop.RemoveAt(0);
                        curPop.Add(bestTour);
                    }
                }
            }

              public void GA_OX_VNS_2Opt(ref PathTour bestTour, CityObject[] coor, string outFile)
            {
                int countEvaluate = 0;
                int i = 0;
                initPathRepresetations();
                createRandomPopulation(_numCity, ref bestTour, coor, ref countEvaluate, ran);
                Evaluate eval = new Evaluate();
                _totalFitness = eval.totalFitnessPopulation(currentPathPop, _populationSize);
                ReadWriteFile writeFile = new ReadWriteFile();
                if (File.Exists(outFile))
                {
                    File.Delete(outFile);
                }
                //for (int i = 0; i < _generationSize; i++)
                writeFile.writeBestFitnessInPop(outFile, currentPathPop, _totalFitness, i + 1, bestTour);
                while (countEvaluate <= _numEvaluate)
                {
                    createNextPopulation_OX_VNS(ref currentPathPop, nextPathPop, ref bestTour, _numCity, _populationSize, _touranmentSize,
                        _mutationRate, _crossoverRate, _percentVNSApplyLocalsearch, coor, ref countEvaluate, _numEvaluate, ran);
                    i++;
                    _totalFitness = eval.totalFitnessPopulation(currentPathPop, _populationSize);
                    writeFile.writeBestFitnessInPop(outFile, currentPathPop, _totalFitness, i + 1, bestTour);

                    Console.WriteLine("      + Number of evaluation: " + countEvaluate);
                }
            }        

            #endregion

            #region Insert Neighborhood
           /*
            *Insertion neighborhood includes all the tours that can be obtained from the given one by removing a vertex and inserting 
            *it at some other position. It can be viewed as a special case of 3opt
            */
            public void applyVNS_Insert(ref List<PathTour> curPop, int numTourApplyLocalSearch, int numCity, CityObject[] citiesCoor, ref int countEvaluate,
                    int totalEvaluate, ref PathTour btour, Random ran)
            {
                int pos = 0;
                VariableNeighborHoodSearch vns = new VariableNeighborHoodSearch();
                if (numTourApplyLocalSearch == 100)
                {
                    List<PathTour> lstTemp = new List<PathTour>();
                    for (int jj = 0; jj < _populationSize; jj++)
                    {
                        PathTour temp = new PathTour(numCity);
                        temp = (PathTour)(curPop[jj]).Clone();
                        temp = vns.VNS_Insert_Neighborhood(temp, citiesCoor, ref btour, ref countEvaluate, totalEvaluate, ran);
                        lstTemp.Add(temp);
                        if (countEvaluate > totalEvaluate)
                        {
                            break;
                        }
                        curPop.Clear();
                        for (int i = 0; i < lstTemp.Count; i++)
                        {
                            curPop.Add(lstTemp[i]);
                        }
                    }
                }
                else
                {
                    for (int jj = 0; jj < numTourApplyLocalSearch; jj++)
                    {
                        PathTour temp = new PathTour(numCity);
                        pos = ran.Next(_populationSize);
                        temp = (PathTour)(curPop[pos]).Clone();
                        curPop.RemoveAt(pos);
                        temp = vns.VNS_Insert_Neighborhood(temp, citiesCoor, ref btour, ref countEvaluate, totalEvaluate, ran);
                        curPop.Add(temp);
                        if (countEvaluate > totalEvaluate)
                        {
                            break;
                        }
                    }
                }
            }

            public void createNextPopulation_OX_VNS_Insert(ref List<PathTour> curPop, List<PathTour> nextPop, ref PathTour bestTour, int numCity, int popSize,
                  int touranmentSize, double mutationRate, double crossRate, int percentApplyLocalsearch, CityObject[] coor, ref int countEvaluate, int totalEvaluate, Random ran)
            {
                int index = 0;
                int pidx1, pidx2;
                PathTour parent1 = new PathTour(numCity);
                PathTour parent2 = new PathTour(numCity);
                PathTour child1 = new PathTour(numCity);
                PathTour child2 = new PathTour(numCity);
                nextPop.Clear();
                Selections sl = new Selections();
                for (int i = 0; i < popSize; i += 2)
                {
                    pidx1 = sl.touranmentSelection(ran, curPop, popSize, touranmentSize);
                    pidx2 = sl.touranmentSelection(ran, curPop, popSize, touranmentSize);
                    parent1 = curPop[pidx1];
                    parent2 = curPop[pidx2];
                    if ((float)(ran.NextDouble()) < _crossoverRate)
                    {
                        Crossovers cross = new Crossovers();
                        cross.orderCrossover(parent1, parent2, out child1, out child2, ran);
                        Mutation mutation = new Mutation();
                        mutation.inversionMutation(ref child1, numCity, mutationRate, ran);
                        mutation.inversionMutation(ref child2, numCity, mutationRate, ran);

                        Evaluate eval = new Evaluate();
                        eval.calculateFitness(ref child1, ref bestTour, numCity, coor);
                        nextPop.Add(child1);
                        eval.calculateFitness(ref child2, ref bestTour, numCity, coor);
                        nextPop.Add(child2);
                        countEvaluate = countEvaluate + 2;
                        if (countEvaluate > totalEvaluate)
                        {
                            break;
                        }
                    }
                    else
                    {
                        nextPop.Add(parent1);
                        nextPop.Add(parent2);
                    }
                }
                applyVNS_Insert(ref nextPop, (int)(percentApplyLocalsearch * _populationSize / 100), numCity, coor, ref countEvaluate, totalEvaluate, ref bestTour, ran);

                if (countEvaluate > totalEvaluate)
                {
                    return;
                }

                curPop.Clear();
                for (int i = 0; i < _populationSize; i++)
                {
                    curPop.Add(nextPop[i]);
                    if ((nextPop[i]).Fitness < (nextPop[index]).Fitness)
                    {
                        index = i;
                    }
                }
                if (_elitism)
                {
                    if (!compareTour(bestTour, curPop[index], numCity))
                    {
                        curPop.RemoveAt(0);
                        curPop.Add(bestTour);
                    }
                }
            }

            public void GA_OX_VNS_Insert(ref PathTour bestTour, CityObject[] coor, string outFile)
            {
                int countEvaluate = 0;
                int i = 0;
                initPathRepresetations();
                createRandomPopulation(_numCity, ref bestTour, coor, ref countEvaluate, ran);
                Evaluate eval = new Evaluate();
                _totalFitness = eval.totalFitnessPopulation(currentPathPop, _populationSize);
                ReadWriteFile writeFile = new ReadWriteFile();
                if (File.Exists(outFile))
                {
                    File.Delete(outFile);
                }
                //for (int i = 0; i < _generationSize; i++)
                writeFile.writeBestFitnessInPop(outFile, currentPathPop, _totalFitness, i + 1, bestTour);
                while (countEvaluate <= _numEvaluate)
                {
                    createNextPopulation_OX_VNS_Insert(ref currentPathPop, nextPathPop, ref bestTour, _numCity, _populationSize, _touranmentSize,
                        _mutationRate, _crossoverRate, _percentVNSApplyLocalsearch, coor, ref countEvaluate, _numEvaluate, ran);
                    i++;
                    _totalFitness = eval.totalFitnessPopulation(currentPathPop, _populationSize);
                    writeFile.writeBestFitnessInPop(outFile, currentPathPop, _totalFitness, i + 1, bestTour);

                    Console.WriteLine("      + Number of evaluation: " + countEvaluate);
                }
            }
        
            #endregion

            #region Swap Neighborhood

            /**************************************************************************************************************************************/
            /*Swap (also known as Exchange) neighborhood includes all the tours that can be obtained from the given one by swapping two vertices. */
            /* It can be viewed as a special case of 4-opt.                                                                                       */
            /**************************************************************************************************************************************/

            public void applyVNS_Swap(ref List<PathTour> curPop, int numTourApplyLocalSearch, int numCity, CityObject[] citiesCoor, ref int countEvaluate,
                int totalEvaluate, ref PathTour btour, Random ran)
            {
                int pos = 0;
                VariableNeighborHoodSearch vns = new VariableNeighborHoodSearch();
                if (numTourApplyLocalSearch == 100)
                {
                    List<PathTour> lstTemp = new List<PathTour>();
                    for (int jj = 0; jj < _populationSize; jj++)
                    {
                        if (countEvaluate > totalEvaluate)
                        {
                            break;
                        }
                        PathTour temp = new PathTour(numCity);
                        temp = (PathTour)(curPop[jj]).Clone();
                        temp = vns.VNS_SwapNeighborhood(temp, citiesCoor, ref btour, ref countEvaluate, totalEvaluate, ran);
                        lstTemp.Add(temp);                       
                    }
                    curPop.Clear();
                    for (int i = 0; i < lstTemp.Count; i++)
                    {
                        curPop.Add(lstTemp[i]);
                    }
                }
                else
                {
                    for (int jj = 0; jj < numTourApplyLocalSearch; jj++)
                    {
                        if (countEvaluate > totalEvaluate)
                        {
                            break;
                        }
                        PathTour temp = new PathTour(numCity);
                        pos = ran.Next(_populationSize);
                        temp = (PathTour)(curPop[pos]).Clone();
                        curPop.RemoveAt(pos);
                        temp = vns.VNS_SwapNeighborhood(temp, citiesCoor, ref btour, ref countEvaluate, totalEvaluate, ran);
                        curPop.Add(temp);                       
                    }
                }
            }

            public void createNextPopulation_OX_VNS_Swap(ref List<PathTour> curPop, List<PathTour> nextPop, ref PathTour bestTour, int numCity, int popSize,
                 int touranmentSize, double mutationRate, double crossRate, int percentApplyLocalsearch, CityObject[] coor, ref int countEvaluate,
                int totalEvaluate, Random ran)
            {
                int index = 0;
                int pidx1, pidx2;
                PathTour parent1 = new PathTour(numCity);
                PathTour parent2 = new PathTour(numCity);
                PathTour child1 = new PathTour(numCity);
                PathTour child2 = new PathTour(numCity);
                nextPop.Clear();
                Selections sl = new Selections();
                for (int i = 0; i < popSize; i += 2)
                {
                    pidx1 = sl.touranmentSelection(ran, curPop, popSize, touranmentSize);
                    pidx2 = sl.touranmentSelection(ran, curPop, popSize, touranmentSize);
                    parent1 = curPop[pidx1];
                    parent2 = curPop[pidx2];
                    if ((float)(ran.NextDouble()) < _crossoverRate)
                    {
                        if (countEvaluate > totalEvaluate)
                        {
                            break;
                        }
                        Crossovers cross = new Crossovers();
                        cross.orderCrossover(parent1, parent2, out child1, out child2, ran);
                        Mutation mutation = new Mutation();
                        mutation.inversionMutation(ref child1, numCity, mutationRate, ran);
                        mutation.inversionMutation(ref child2, numCity, mutationRate, ran);

                        Evaluate eval = new Evaluate();
                        eval.calculateFitness(ref child1, ref bestTour, numCity, coor);
                        nextPop.Add(child1);
                        eval.calculateFitness(ref child2, ref bestTour, numCity, coor);
                        nextPop.Add(child2);
                        countEvaluate = countEvaluate + 2;                        
                    }
                    else
                    {
                        nextPop.Add(parent1);
                        nextPop.Add(parent2);
                    }
                }
                applyVNS_Swap(ref nextPop, (int)(percentApplyLocalsearch * _populationSize / 100), numCity, coor, ref countEvaluate, totalEvaluate, ref bestTour, ran);

                if (countEvaluate > totalEvaluate)
                {
                    return;
                }

                curPop.Clear();
                for (int i = 0; i < _populationSize; i++)
                {
                    curPop.Add(nextPop[i]);
                    if ((nextPop[i]).Fitness < (nextPop[index]).Fitness)
                    {
                        index = i;
                    }
                }
                if (_elitism)
                {
                    if (!compareTour(bestTour, curPop[index], numCity))
                    {
                        curPop.RemoveAt(0);
                        curPop.Add(bestTour);
                    }
                }
            }

            public void GA_OX_VNS_Swap(ref PathTour bestTour, CityObject[] coor, string outFile)
            {
                int countEvaluate = 0;
                int i = 0;
                initPathRepresetations();
                createRandomPopulation(_numCity, ref bestTour, coor, ref countEvaluate, ran);
                Evaluate eval = new Evaluate();
                _totalFitness = eval.totalFitnessPopulation(currentPathPop, _populationSize);
                ReadWriteFile writeFile = new ReadWriteFile();
                if (File.Exists(outFile))
                {
                    File.Delete(outFile);
                }
                //for (int i = 0; i < _generationSize; i++)
                writeFile.writeBestFitnessInPop(outFile, currentPathPop, _totalFitness, i + 1, bestTour);
                while (countEvaluate <= _numEvaluate)
                {
                    createNextPopulation_OX_VNS_Swap(ref currentPathPop, nextPathPop, ref bestTour, _numCity, _populationSize, _touranmentSize,
                        _mutationRate, _crossoverRate, _percentVNSApplyLocalsearch, coor, ref countEvaluate, _numEvaluate, ran);
                    i++;
                    _totalFitness = eval.totalFitnessPopulation(currentPathPop, _populationSize);
                    writeFile.writeBestFitnessInPop(outFile, currentPathPop, _totalFitness, i, bestTour);

                    Console.WriteLine("      + Number of evaluation: " + countEvaluate);
                }
            }
        
            #endregion                         

            #region SwapInSegment
            /********************************************************************************/
            /* Chia tour lam numSgm doan
             * Chia Tour lam m=kmax=numSgm doan                                           */
            /* Nk duoc thuc hien boi doi vi tri 2 thanh pho ke nhau trong cac doan tu 1..k  */
            /********************************************************************************/
            public void applyVNS_SwapInSegment(ref List<PathTour> curPop, int numCity, CityObject[] citiesCoor, int numSgm, int percentApplyLocalsearch, ref int countEvaluate,
             int totalEvaluate, ref PathTour btour, Random ran)
            {
                int pos = 0;
                int kmax = numSgm;
                int sgm_Length = numCity / numSgm;
                VariableNeighborHoodSearch vns = new VariableNeighborHoodSearch();
                if (percentApplyLocalsearch == 100)
                {
                    List<PathTour> lstTemp = new List<PathTour>();
                    for (int jj = 0; jj < _populationSize; jj++)
                    {
                        if (countEvaluate > totalEvaluate)
                        {
                            break;
                        }
                        PathTour temp = new PathTour(numCity);
                        temp = (PathTour)(curPop[jj]).Clone();
                        temp = vns.VNS_SwapInSegment_1(temp, kmax, sgm_Length, citiesCoor, ref btour, ref countEvaluate, totalEvaluate, ran);
                        lstTemp.Add(temp);
                    }
                    curPop.Clear();
                    for (int i = 0; i < lstTemp.Count; i++)
                    {
                        curPop.Add(lstTemp[i]);
                    }
                }
                else
                {
                    int numTourApplyLocalSearch = (int)(percentApplyLocalsearch * _populationSize / 100);
                    for (int jj = 0; jj < numTourApplyLocalSearch; jj++)
                    {
                        if (countEvaluate > totalEvaluate)
                        {
                            break;
                        }
                        PathTour temp = new PathTour(numCity);
                        pos = ran.Next(_populationSize);
                        temp = (PathTour)(curPop[pos]).Clone();
                        curPop.RemoveAt(pos);
                        temp = vns.VNS_SwapInSegment_1(temp, kmax, sgm_Length, citiesCoor, ref btour, ref countEvaluate, totalEvaluate, ran);
                        curPop.Add(temp);
                    }
                }
            }

            public void createNextPopulation_OX_VNS_SwapInSegment(ref List<PathTour> curPop, List<PathTour> nextPop, ref PathTour bestTour, int numCity, int popSize,
              int touranmentSize, double mutationRate, double crossRate, int numSgm, int percentApplyLocalsearch, CityObject[] coor, ref int countEvaluate,
              int totalEvaluate, Random ran)
            {
                int index = 0;
                int pidx1, pidx2;
                PathTour parent1 = new PathTour(numCity);
                PathTour parent2 = new PathTour(numCity);
                PathTour child1 = new PathTour(numCity);
                PathTour child2 = new PathTour(numCity);
                nextPop.Clear();
                Selections sl = new Selections();
                for (int i = 0; i < popSize; i += 2)
                {
                    if (countEvaluate > totalEvaluate)
                    {
                        break;
                    }
                    pidx1 = sl.touranmentSelection(ran, curPop, popSize, touranmentSize);
                    pidx2 = sl.touranmentSelection(ran, curPop, popSize, touranmentSize);
                    parent1 = curPop[pidx1];
                    parent2 = curPop[pidx2];
                    if ((float)(ran.NextDouble()) < _crossoverRate)
                    {
                        Crossovers cross = new Crossovers();
                        cross.orderCrossover(parent1, parent2, out child1, out child2, ran);
                        Mutation mutation = new Mutation();
                        mutation.inversionMutation(ref child1, numCity, mutationRate, ran);
                        mutation.inversionMutation(ref child2, numCity, mutationRate, ran);

                        Evaluate eval = new Evaluate();
                        eval.calculateFitness(ref child1, ref bestTour, numCity, coor);
                        nextPop.Add(child1);
                        eval.calculateFitness(ref child2, ref bestTour, numCity, coor);
                        nextPop.Add(child2);
                        countEvaluate = countEvaluate + 2;
                    }
                    else
                    {
                        nextPop.Add(parent1);
                        nextPop.Add(parent2);
                    }
                }
                applyVNS_SwapInSegment(ref nextPop, numCity, coor, numSgm, percentApplyLocalsearch, ref countEvaluate, totalEvaluate, ref bestTour, ran);

                if (countEvaluate > totalEvaluate)
                {
                    return;
                }

                curPop.Clear();
                for (int i = 0; i < _populationSize; i++)
                {
                    curPop.Add(nextPop[i]);
                    if ((nextPop[i]).Fitness < (nextPop[index]).Fitness)
                    {
                        index = i;
                    }
                }
                if (_elitism)
                {
                    if (!compareTour(bestTour, curPop[index], numCity))
                    {
                        curPop.RemoveAt(0);
                        curPop.Add(bestTour);
                    }
                }
            }

            public void GA_OX_VNS_SwapInSegment(ref PathTour bestTour, CityObject[] coor, string outFile, int numSgm)
            {
                int countEvaluate = 0;
                int i = 0;
                initPathRepresetations();
                createRandomPopulation(_numCity, ref bestTour, coor, ref countEvaluate, ran);
                Evaluate eval = new Evaluate();
                _totalFitness = eval.totalFitnessPopulation(currentPathPop, _populationSize);
                ReadWriteFile writeFile = new ReadWriteFile();
                if (File.Exists(outFile))
                {
                    File.Delete(outFile);
                }
                //for (int i = 0; i < _generationSize; i++)
                writeFile.writeBestFitnessInPop(outFile, currentPathPop, _totalFitness, i + 1, bestTour);
                while (countEvaluate <= _numEvaluate)
                {
                    createNextPopulation_OX_VNS_SwapInSegment(ref currentPathPop, nextPathPop, ref bestTour, _numCity, _populationSize, _touranmentSize,
                        _mutationRate, _crossoverRate, numSgm, _percentVNSApplyLocalsearch, coor, ref countEvaluate, _numEvaluate, ran);
                    i++;
                    _totalFitness = eval.totalFitnessPopulation(currentPathPop, _populationSize);
                    writeFile.writeBestFitnessInPop(outFile, currentPathPop, _totalFitness, i, bestTour);

                    Console.WriteLine("      + Number of evaluation: " + countEvaluate);
                }
            }

            #endregion        

            #region Segment
            /****************************************************************************/
            /* Chia Tour lam numSgm doan                                                */
            /* Nk duoc thuc hien boi: lam dao lon vi tri cac thanh pho trong doan 1..k  */
            /****************************************************************************/
            public void applyVNS_Segment(ref List<PathTour> curPop, int numCity, CityObject[] citiesCoor, int numSgm, int percentApplyLocalsearch, ref int countEvaluate,
                                        int totalEvaluate, ref PathTour btour, Random ran)
            {
                int pos = 0;
                int kmax = numSgm;
                int sgm_Length = numCity / numSgm;
                VariableNeighborHoodSearch vns = new VariableNeighborHoodSearch();
                if (percentApplyLocalsearch == 100)
                {
                    List<PathTour> lstTemp = new List<PathTour>();
                    for (int jj = 0; jj < _populationSize; jj++)
                    {
                        if (countEvaluate > totalEvaluate)
                        {
                            break;
                        }
                        PathTour temp = new PathTour(numCity);
                        temp = (PathTour)(curPop[jj]).Clone();
                        temp = vns.VNS_InSegment_1(temp, kmax, sgm_Length, citiesCoor, ref btour, ref countEvaluate, totalEvaluate, ran);
                        lstTemp.Add(temp);
                    }
                    curPop.Clear();
                    for (int i = 0; i < lstTemp.Count; i++)
                    {
                        curPop.Add(lstTemp[i]);
                    }
                }
                else
                {
                    int numTourApplyLocalSearch = (int)(percentApplyLocalsearch * _populationSize / 100);
                    for (int jj = 0; jj < numTourApplyLocalSearch; jj++)
                    {
                        if (countEvaluate > totalEvaluate)
                        {
                            break;
                        }
                        PathTour temp = new PathTour(numCity);
                        pos = ran.Next(_populationSize);
                        temp = (PathTour)(curPop[pos]).Clone();
                        curPop.RemoveAt(pos);
                        temp = vns.VNS_InSegment_1(temp, kmax, sgm_Length, citiesCoor, ref btour, ref countEvaluate, totalEvaluate, ran);
                        curPop.Add(temp);
                    }
                }
            }

            public void createNextPopulation_OX_VNS_Segment(ref List<PathTour> curPop, List<PathTour> nextPop, ref PathTour bestTour, int numCity, int popSize,
              int touranmentSize, double mutationRate, double crossRate, int numSgm, int percentApplyLocalsearch, CityObject[] coor, ref int countEvaluate,
              int totalEvaluate, Random ran)
            {
                int index = 0;
                int pidx1, pidx2;
                PathTour parent1 = new PathTour(numCity);
                PathTour parent2 = new PathTour(numCity);
                PathTour child1 = new PathTour(numCity);
                PathTour child2 = new PathTour(numCity);
                nextPop.Clear();
                Selections sl = new Selections();
                for (int i = 0; i < popSize; i += 2)
                {
                    if (countEvaluate > totalEvaluate)
                    {
                        break;
                    }
                    pidx1 = sl.touranmentSelection(ran, curPop, popSize, touranmentSize);
                    pidx2 = sl.touranmentSelection(ran, curPop, popSize, touranmentSize);
                    parent1 = curPop[pidx1];
                    parent2 = curPop[pidx2];
                    if ((float)(ran.NextDouble()) < _crossoverRate)
                    {
                        Crossovers cross = new Crossovers();
                        cross.orderCrossover(parent1, parent2, out child1, out child2, ran);
                        Mutation mutation = new Mutation();
                        mutation.inversionMutation(ref child1, numCity, mutationRate, ran);
                        mutation.inversionMutation(ref child2, numCity, mutationRate, ran);

                        Evaluate eval = new Evaluate();
                        eval.calculateFitness(ref child1, ref bestTour, numCity, coor);
                        nextPop.Add(child1);
                        eval.calculateFitness(ref child2, ref bestTour, numCity, coor);
                        nextPop.Add(child2);
                        countEvaluate = countEvaluate + 2;
                    }
                    else
                    {
                        nextPop.Add(parent1);
                        nextPop.Add(parent2);
                    }
                }
                applyVNS_Segment(ref nextPop, numCity, coor, numSgm, percentApplyLocalsearch, ref countEvaluate, totalEvaluate, ref bestTour, ran);

                if (countEvaluate > totalEvaluate)
                {
                    return;
                }

                curPop.Clear();
                for (int i = 0; i < _populationSize; i++)
                {
                    curPop.Add(nextPop[i]);
                    if ((nextPop[i]).Fitness < (nextPop[index]).Fitness)
                    {
                        index = i;
                    }
                }
                if (_elitism)
                {
                    if (!compareTour(bestTour, curPop[index], numCity))
                    {
                        curPop.RemoveAt(0);
                        curPop.Add(bestTour);
                    }
                }
            }

            public void GA_OX_VNS_Segment(ref PathTour bestTour, CityObject[] coor, string outFile, int numSgm)
            {
                int countEvaluate = 0;
                int i = 0;
                initPathRepresetations();
                createRandomPopulation(_numCity, ref bestTour, coor, ref countEvaluate, ran);
                Evaluate eval = new Evaluate();
                _totalFitness = eval.totalFitnessPopulation(currentPathPop, _populationSize);
                ReadWriteFile writeFile = new ReadWriteFile();
                if (File.Exists(outFile))
                {
                    File.Delete(outFile);
                }
                //for (int i = 0; i < _generationSize; i++)
                writeFile.writeBestFitnessInPop(outFile, currentPathPop, _totalFitness, i + 1, bestTour);
                while (countEvaluate <= _numEvaluate)
                {
                    createNextPopulation_OX_VNS_Segment(ref currentPathPop, nextPathPop, ref bestTour, _numCity, _populationSize, _touranmentSize,
                        _mutationRate, _crossoverRate, numSgm, _percentVNSApplyLocalsearch, coor, ref countEvaluate, _numEvaluate, ran);
                    i++;
                    _totalFitness = eval.totalFitnessPopulation(currentPathPop, _populationSize);
                    writeFile.writeBestFitnessInPop(outFile, currentPathPop, _totalFitness, i, bestTour);

                    Console.WriteLine("      + Number of evaluation: " + countEvaluate);
                }
            }

            #endregion        

            #region Swap Segment

            /****************************************************************************/
            /* Chia Tour lam m=kmax=ncity/10 doan                                       */
            /* Nk duoc thuc hien boi: dao k lan vi tri cua 2 doan (segment)             */
            /****************************************************************************/
            public void applyVNS_SwapSegment(ref List<PathTour> curPop, int numCity, CityObject[] citiesCoor, int numSgm, int percentApplyLocalsearch, ref int countEvaluate,
                                         int totalEvaluate, ref PathTour btour, Random ran)
            {
                int pos = 0;
                int kmax = numSgm;
                VariableNeighborHoodSearch vns = new VariableNeighborHoodSearch();
                if (percentApplyLocalsearch == 100)
                {
                    List<PathTour> lstTemp = new List<PathTour>();
                    for (int jj = 0; jj < _populationSize; jj++)
                    {
                        if (countEvaluate > totalEvaluate)
                        {
                            break;
                        }
                        PathTour temp = new PathTour(numCity);
                        temp = (PathTour)(curPop[jj]).Clone();
                        temp = vns.VNS_SwapSegment(temp, kmax, numSgm, citiesCoor, ref btour, ref countEvaluate, totalEvaluate, ran);
                        lstTemp.Add(temp);
                    }
                    curPop.Clear();
                    for (int i = 0; i < lstTemp.Count; i++)
                    {
                        curPop.Add(lstTemp[i]);
                    }
                }
                else
                {
                    int numTourApplyLocalSearch = (int)(percentApplyLocalsearch * _populationSize / 100);
                    for (int jj = 0; jj < numTourApplyLocalSearch; jj++)
                    {
                        if (countEvaluate > totalEvaluate)
                        {
                            break;
                        }
                        PathTour temp = new PathTour(numCity);
                        pos = ran.Next(_populationSize);
                        temp = (PathTour)(curPop[pos]).Clone();
                        curPop.RemoveAt(pos);
                        temp = vns.VNS_SwapSegment(temp, kmax, numSgm, citiesCoor, ref btour, ref countEvaluate, totalEvaluate, ran);
                        curPop.Add(temp);
                    }
                }
            }


            public void createNextPopulation_OX_VNS_SwapSegment(ref List<PathTour> curPop, List<PathTour> nextPop, ref PathTour bestTour, int numCity, int popSize,
                int touranmentSize, double mutationRate, double crossRate, int numSgm, int percentApplyLocalsearch, CityObject[] coor, ref int countEvaluate,
                int totalEvaluate, Random ran)
            {
                int index = 0;
                int pidx1, pidx2;
                PathTour parent1 = new PathTour(numCity);
                PathTour parent2 = new PathTour(numCity);
                PathTour child1 = new PathTour(numCity);
                PathTour child2 = new PathTour(numCity);
                nextPop.Clear();
                Selections sl = new Selections();
                for (int i = 0; i < popSize; i += 2)
                {
                    if (countEvaluate > totalEvaluate)
                    {
                        break;
                    }
                    pidx1 = sl.touranmentSelection(ran, curPop, popSize, touranmentSize);
                    pidx2 = sl.touranmentSelection(ran, curPop, popSize, touranmentSize);
                    parent1 = curPop[pidx1];
                    parent2 = curPop[pidx2];
                    if ((float)(ran.NextDouble()) < _crossoverRate)
                    {
                        Crossovers cross = new Crossovers();
                        cross.orderCrossover(parent1, parent2, out child1, out child2, ran);
                        Mutation mutation = new Mutation();
                        mutation.inversionMutation(ref child1, numCity, mutationRate, ran);
                        mutation.inversionMutation(ref child2, numCity, mutationRate, ran);

                        Evaluate eval = new Evaluate();
                        eval.calculateFitness(ref child1, ref bestTour, numCity, coor);
                        nextPop.Add(child1);
                        eval.calculateFitness(ref child2, ref bestTour, numCity, coor);
                        nextPop.Add(child2);
                        countEvaluate = countEvaluate + 2;
                    }
                    else
                    {
                        nextPop.Add(parent1);
                        nextPop.Add(parent2);
                    }
                }
                applyVNS_SwapSegment(ref nextPop, numCity, coor, numSgm, percentApplyLocalsearch, ref countEvaluate, totalEvaluate, ref bestTour, ran);

                if (countEvaluate > totalEvaluate)
                {
                    return;
                }

                curPop.Clear();
                for (int i = 0; i < _populationSize; i++)
                {
                    curPop.Add(nextPop[i]);
                    if ((nextPop[i]).Fitness < (nextPop[index]).Fitness)
                    {
                        index = i;
                    }
                }
                if (_elitism)
                {
                    if (!compareTour(bestTour, curPop[index], numCity))
                    {
                        curPop.RemoveAt(0);
                        curPop.Add(bestTour);
                    }
                }
            }


            public void GA_OX_VNS_SwapSegment(ref PathTour bestTour, CityObject[] coor, string outFile, int numSgm)
            {
                int countEvaluate = 0;
                int i = 0;
                initPathRepresetations();
                createRandomPopulation(_numCity, ref bestTour, coor, ref countEvaluate, ran);
                Evaluate eval = new Evaluate();
                _totalFitness = eval.totalFitnessPopulation(currentPathPop, _populationSize);
                ReadWriteFile writeFile = new ReadWriteFile();
                if (File.Exists(outFile))
                {
                    File.Delete(outFile);
                }
                //for (int i = 0; i < _generationSize; i++)
                writeFile.writeBestFitnessInPop(outFile, currentPathPop, _totalFitness, i + 1, bestTour);
                while (countEvaluate <= _numEvaluate)
                {
                    createNextPopulation_OX_VNS_SwapSegment(ref currentPathPop, nextPathPop, ref bestTour, _numCity, _populationSize, _touranmentSize,
                        _mutationRate, _crossoverRate, numSgm, _percentVNSApplyLocalsearch, coor, ref countEvaluate, _numEvaluate, ran);
                    i++;
                    _totalFitness = eval.totalFitnessPopulation(currentPathPop, _populationSize);
                    writeFile.writeBestFitnessInPop(outFile, currentPathPop, _totalFitness, i, bestTour);

                    Console.WriteLine("      + Number of evaluation: " + countEvaluate);
                }
            }
            #endregion
        
            #region SwapSegment + SwapInSegment
            /****************************************************************************************************************/
            /* Chia Tour lam m=kmax=ncity/10 doan                                                                           */
            /* Nk duoc thuc hien boi: dao k lan vi tri cua 2 doan (segment) va trong moi doan se dao lon vi tri ca phan tu  */
            /****************************************************************************************************************/
            public void applyVNS_CombineSwapAndMoveSegment(ref List<PathTour> curPop, int numCity, CityObject[] citiesCoor, int numSgm, int percentApplyLocalsearch, ref int countEvaluate,
                                       int totalEvaluate, ref PathTour btour, Random ran)
            {
                int pos = 0;
                int kmax = numSgm;
                VariableNeighborHoodSearch vns = new VariableNeighborHoodSearch();
                if (percentApplyLocalsearch == 100)
                {
                    List<PathTour> lstTemp = new List<PathTour>();
                    for (int jj = 0; jj < _populationSize; jj++)
                    {
                        if (countEvaluate > totalEvaluate)
                        {
                            break;
                        }
                        PathTour temp = new PathTour(numCity);
                        temp = (PathTour)(curPop[jj]).Clone();
                        temp = vns.VNS_CombineSwapAndMoveSegment(temp, kmax, numSgm, citiesCoor, ref btour, ref countEvaluate, totalEvaluate, ran);
                        lstTemp.Add(temp);
                    }
                    curPop.Clear();
                    for (int i = 0; i < lstTemp.Count; i++)
                    {
                        curPop.Add(lstTemp[i]);
                    }
                }
                else
                {
                    int numTourApplyLocalSearch = (int)(percentApplyLocalsearch * _populationSize / 100);
                    for (int jj = 0; jj < numTourApplyLocalSearch; jj++)
                    {
                        if (countEvaluate > totalEvaluate)
                        {
                            break;
                        }
                        PathTour temp = new PathTour(numCity);
                        pos = ran.Next(_populationSize);
                        temp = (PathTour)(curPop[pos]).Clone();
                        curPop.RemoveAt(pos);
                        temp = vns.VNS_CombineSwapAndMoveSegment(temp, kmax, numSgm, citiesCoor, ref btour, ref countEvaluate, totalEvaluate, ran);
                        curPop.Add(temp);
                    }
                }
            }


            public void createNextPopulation_OX_VNS_CombineSwapAndMoveSegment(ref List<PathTour> curPop, List<PathTour> nextPop, ref PathTour bestTour, int numCity, int popSize,
                int touranmentSize, double mutationRate, double crossRate, int numSgm, int percentApplyLocalsearch, CityObject[] coor, ref int countEvaluate,
                int totalEvaluate, Random ran)
            {
                int index = 0;
                int pidx1, pidx2;
                PathTour parent1 = new PathTour(numCity);
                PathTour parent2 = new PathTour(numCity);
                PathTour child1 = new PathTour(numCity);
                PathTour child2 = new PathTour(numCity);
                nextPop.Clear();
                Selections sl = new Selections();
                for (int i = 0; i < popSize; i += 2)
                {
                    if (countEvaluate > totalEvaluate)
                    {
                        break;
                    }
                    pidx1 = sl.touranmentSelection(ran, curPop, popSize, touranmentSize);
                    pidx2 = sl.touranmentSelection(ran, curPop, popSize, touranmentSize);
                    parent1 = curPop[pidx1];
                    parent2 = curPop[pidx2];
                    if ((float)(ran.NextDouble()) < _crossoverRate)
                    {
                        Crossovers cross = new Crossovers();
                        cross.orderCrossover(parent1, parent2, out child1, out child2, ran);
                        Mutation mutation = new Mutation();
                        mutation.inversionMutation(ref child1, numCity, mutationRate, ran);
                        mutation.inversionMutation(ref child2, numCity, mutationRate, ran);

                        Evaluate eval = new Evaluate();
                        eval.calculateFitness(ref child1, ref bestTour, numCity, coor);
                        nextPop.Add(child1);
                        eval.calculateFitness(ref child2, ref bestTour, numCity, coor);
                        nextPop.Add(child2);
                        countEvaluate = countEvaluate + 2;
                    }
                    else
                    {
                        nextPop.Add(parent1);
                        nextPop.Add(parent2);
                    }
                }
                applyVNS_CombineSwapAndMoveSegment(ref nextPop, numCity, coor, numSgm, percentApplyLocalsearch, ref countEvaluate, totalEvaluate, ref bestTour, ran);

                if (countEvaluate > totalEvaluate)
                {
                    return;
                }

                curPop.Clear();
                for (int i = 0; i < _populationSize; i++)
                {
                    curPop.Add(nextPop[i]);
                    if ((nextPop[i]).Fitness < (nextPop[index]).Fitness)
                    {
                        index = i;
                    }
                }
                if (_elitism)
                {
                    if (!compareTour(bestTour, curPop[index], numCity))
                    {
                        curPop.RemoveAt(0);
                        curPop.Add(bestTour);
                    }
                }
            }


            public void GA_OX_VNS_CombineSwapAndMoveSegment(ref PathTour bestTour, CityObject[] coor, string outFile, int numSgm)
            {
                int countEvaluate = 0;
                int i = 0;
                initPathRepresetations();
                createRandomPopulation(_numCity, ref bestTour, coor, ref countEvaluate, ran);
                Evaluate eval = new Evaluate();
                _totalFitness = eval.totalFitnessPopulation(currentPathPop, _populationSize);
                ReadWriteFile writeFile = new ReadWriteFile();
                if (File.Exists(outFile))
                {
                    File.Delete(outFile);
                }
                //for (int i = 0; i < _generationSize; i++)
                writeFile.writeBestFitnessInPop(outFile, currentPathPop, _totalFitness, i + 1, bestTour);
                while (countEvaluate <= _numEvaluate)
                {
                    createNextPopulation_OX_VNS_CombineSwapAndMoveSegment(ref currentPathPop, nextPathPop, ref bestTour, _numCity, _populationSize, _touranmentSize,
                        _mutationRate, _crossoverRate, numSgm, _percentVNSApplyLocalsearch, coor, ref countEvaluate, _numEvaluate, ran);
                    i++;
                    _totalFitness = eval.totalFitnessPopulation(currentPathPop, _populationSize);
                    writeFile.writeBestFitnessInPop(outFile, currentPathPop, _totalFitness, i, bestTour);

                    Console.WriteLine("      + Number of evaluation: " + countEvaluate);
                }
            }

            #endregion

            #region swapNextCity
            /****************************************************************************/
            /*                                                                          */
            /* Nk duoc thuc hien boi: dao k lan 2 thanh pho ke nhau                     */
            /****************************************************************************/
            public void applyVNS_SwapNextCity(ref List<PathTour> curPop, int numCity, CityObject[] citiesCoor, int percentApplyLocalsearch, ref int countEvaluate,
                                 int totalEvaluate, ref PathTour btour, Random ran)
            {
                int pos = 0;
                int kmax = numCity;
                VariableNeighborHoodSearch vns = new VariableNeighborHoodSearch();
                if (percentApplyLocalsearch == 100)
                {
                    List<PathTour> lstTemp = new List<PathTour>();
                    for (int jj = 0; jj < _populationSize; jj++)
                    {
                        if (countEvaluate > totalEvaluate)
                        {
                            break;
                        }
                        PathTour temp = new PathTour(numCity);
                        temp = (PathTour)(curPop[jj]).Clone();
                        temp = vns.VNS_swapNextCity(temp, kmax, citiesCoor, ref btour, ref countEvaluate, totalEvaluate, ran);
                        lstTemp.Add(temp);
                    }
                    curPop.Clear();
                    for (int i = 0; i < lstTemp.Count; i++)
                    {
                        curPop.Add(lstTemp[i]);
                    }
                }
                else
                {
                    int numTourApplyLocalSearch = (int)(percentApplyLocalsearch * _populationSize / 100);
                    for (int jj = 0; jj < numTourApplyLocalSearch; jj++)
                    {
                        if (countEvaluate > totalEvaluate)
                        {
                            break;
                        }
                        PathTour temp = new PathTour(numCity);
                        pos = ran.Next(_populationSize);
                        temp = (PathTour)(curPop[pos]).Clone();
                        curPop.RemoveAt(pos);
                        temp = vns.VNS_swapNextCity(temp, kmax, citiesCoor, ref btour, ref countEvaluate, totalEvaluate, ran);
                        curPop.Add(temp);
                    }
                }
            }


            public void createNextPopulation_OX_VNS_SwapNextCity(ref List<PathTour> curPop, List<PathTour> nextPop, ref PathTour bestTour, int numCity, int popSize,
                int touranmentSize, double mutationRate, double crossRate, int percentApplyLocalsearch, CityObject[] coor, ref int countEvaluate,
                int totalEvaluate, Random ran)
            {
                int index = 0;
                int pidx1, pidx2;
                PathTour parent1 = new PathTour(numCity);
                PathTour parent2 = new PathTour(numCity);
                PathTour child1 = new PathTour(numCity);
                PathTour child2 = new PathTour(numCity);
                nextPop.Clear();
                Selections sl = new Selections();
                for (int i = 0; i < popSize; i += 2)
                {
                    if (countEvaluate > totalEvaluate)
                    {
                        break;
                    }
                    pidx1 = sl.touranmentSelection(ran, curPop, popSize, touranmentSize);
                    pidx2 = sl.touranmentSelection(ran, curPop, popSize, touranmentSize);
                    parent1 = curPop[pidx1];
                    parent2 = curPop[pidx2];
                    if ((float)(ran.NextDouble()) < _crossoverRate)
                    {
                        Crossovers cross = new Crossovers();
                        cross.orderCrossover(parent1, parent2, out child1, out child2, ran);
                        Mutation mutation = new Mutation();
                        mutation.inversionMutation(ref child1, numCity, mutationRate, ran);
                        mutation.inversionMutation(ref child2, numCity, mutationRate, ran);

                        Evaluate eval = new Evaluate();
                        eval.calculateFitness(ref child1, ref bestTour, numCity, coor);
                        nextPop.Add(child1);
                        eval.calculateFitness(ref child2, ref bestTour, numCity, coor);
                        nextPop.Add(child2);
                        countEvaluate = countEvaluate + 2;
                    }
                    else
                    {
                        nextPop.Add(parent1);
                        nextPop.Add(parent2);
                    }
                }
                applyVNS_SwapNextCity(ref nextPop, numCity, coor, percentApplyLocalsearch, ref countEvaluate, totalEvaluate, ref bestTour, ran);

                if (countEvaluate > totalEvaluate)
                {
                    return;
                }

                curPop.Clear();
                for (int i = 0; i < _populationSize; i++)
                {
                    curPop.Add(nextPop[i]);
                    if ((nextPop[i]).Fitness < (nextPop[index]).Fitness)
                    {
                        index = i;
                    }
                }
                if (_elitism)
                {
                    if (!compareTour(bestTour, curPop[index], numCity))
                    {
                        curPop.RemoveAt(0);
                        curPop.Add(bestTour);
                    }
                }
            }


            public void GA_OX_VNS_SwapNextCity(ref PathTour bestTour, CityObject[] coor, string outFile)
            {
                int countEvaluate = 0;
                int i = 0;
                initPathRepresetations();
                createRandomPopulation(_numCity, ref bestTour, coor, ref countEvaluate, ran);
                Evaluate eval = new Evaluate();
                _totalFitness = eval.totalFitnessPopulation(currentPathPop, _populationSize);
                ReadWriteFile writeFile = new ReadWriteFile();
                if (File.Exists(outFile))
                {
                    File.Delete(outFile);
                }
                //for (int i = 0; i < _generationSize; i++)
                writeFile.writeBestFitnessInPop(outFile, currentPathPop, _totalFitness, i + 1, bestTour);
                while (countEvaluate <= _numEvaluate)
                {
                    createNextPopulation_OX_VNS_SwapNextCity(ref currentPathPop, nextPathPop, ref bestTour, _numCity, _populationSize, _touranmentSize,
                        _mutationRate, _crossoverRate, _percentVNSApplyLocalsearch, coor, ref countEvaluate, _numEvaluate, ran);
                    i++;
                    _totalFitness = eval.totalFitnessPopulation(currentPathPop, _populationSize);
                    writeFile.writeBestFitnessInPop(outFile, currentPathPop, _totalFitness, i, bestTour);

                    Console.WriteLine("      + Number of evaluation: " + countEvaluate);
                }
            }
            #endregion

            #region Ap dung VNS cho p% ca the tot nhat
            public void applyVNSForBestTours(ref List<PathTour> curPop, int numCity, CityObject[] citiesCoor, int percentApplyLocalsearch, ref int countEvaluate,
                              int totalEvaluate, ref PathTour btour, Random ran)
            {
                int pos = 0;
                int kmax = numCity;
                VariableNeighborHoodSearch vns = new VariableNeighborHoodSearch();
                if (percentApplyLocalsearch == 100)
                {
                    //List<PathTour> lstTemp = new List<PathTour>();
                    //for (int jj = 0; jj < _populationSize; jj++)
                    //{
                    //    if (countEvaluate > totalEvaluate)
                    //    {
                    //        break;
                    //    }
                    //    PathTour temp = new PathTour(numCity);
                    //    temp = (PathTour)(curPop[jj]).Clone();
                    //    temp = vns.VNS_swapNextCity(temp, kmax, citiesCoor, ref btour, ref countEvaluate, totalEvaluate, ran);
                    //    lstTemp.Add(temp);
                    //}
                    //curPop.Clear();
                    //for (int i = 0; i < lstTemp.Count; i++)
                    //{
                    //    curPop.Add(lstTemp[i]);
                    //}
                }
                else
                {
                    List<PathTour> tempPop1 = new List<PathTour>();
                    int numTourApplyLocalSearch = (int)(percentApplyLocalsearch * _populationSize / 100);
                    currentPathPop.Sort(new SortAscFitnessPathTour());

                    for (int jj = 0; jj < numTourApplyLocalSearch; jj++)
                    {
                        if (countEvaluate > totalEvaluate)
                        {
                            break;
                        }
                        PathTour temp = new PathTour(numCity);
                        //pos = ran.Next(_populationSize);
                        temp = (PathTour)(curPop[jj]).Clone();
                        //curPop.RemoveAt(pos);
                        temp = vns.VNS_swapNextCity(temp, kmax, citiesCoor, ref btour, ref countEvaluate, totalEvaluate, ran);
                        tempPop1.Add(temp);
                    }
                    for (int jj = numTourApplyLocalSearch; jj < curPop.Count; jj++)
                    {
                        tempPop1.Add(curPop[jj]);
                    }
                    curPop.Clear();
                    for (int jj = 0; jj < tempPop1.Count; jj++)
                    {
                        curPop.Add(tempPop1[jj]);
                    }
                }
            }

            public void createNextPopulation_OX_applyVNSForBestTours(ref List<PathTour> curPop, List<PathTour> nextPop, ref PathTour bestTour, int numCity, int popSize,
          int touranmentSize, double mutationRate, double crossRate, int percentApplyLocalsearch, CityObject[] coor, ref int countEvaluate,
          int totalEvaluate, Random ran)
            {
                int index = 0;
                int pidx1, pidx2;
                PathTour parent1 = new PathTour(numCity);
                PathTour parent2 = new PathTour(numCity);
                PathTour child1 = new PathTour(numCity);
                PathTour child2 = new PathTour(numCity);
                nextPop.Clear();
                Selections sl = new Selections();
                for (int i = 0; i < popSize; i += 2)
                {
                    if (countEvaluate > totalEvaluate)
                    {
                        break;
                    }
                    pidx1 = sl.touranmentSelection(ran, curPop, popSize, touranmentSize);
                    pidx2 = sl.touranmentSelection(ran, curPop, popSize, touranmentSize);
                    parent1 = curPop[pidx1];
                    parent2 = curPop[pidx2];
                    if ((float)(ran.NextDouble()) < _crossoverRate)
                    {
                        Crossovers cross = new Crossovers();
                        cross.orderCrossover(parent1, parent2, out child1, out child2, ran);
                        Mutation mutation = new Mutation();
                        mutation.inversionMutation(ref child1, numCity, mutationRate, ran);
                        mutation.inversionMutation(ref child2, numCity, mutationRate, ran);

                        Evaluate eval = new Evaluate();
                        eval.calculateFitness(ref child1, ref bestTour, numCity, coor);
                        nextPop.Add(child1);
                        eval.calculateFitness(ref child2, ref bestTour, numCity, coor);
                        nextPop.Add(child2);
                        countEvaluate = countEvaluate + 2;
                    }
                    else
                    {
                        nextPop.Add(parent1);
                        nextPop.Add(parent2);
                    }
                }
                applyVNSForBestTours(ref nextPop, numCity, coor, percentApplyLocalsearch, ref countEvaluate, totalEvaluate, ref bestTour, ran);

                if (countEvaluate > totalEvaluate)
                {
                    return;
                }

                curPop.Clear();
                for (int i = 0; i < _populationSize; i++)
                {
                    curPop.Add(nextPop[i]);
                    if ((nextPop[i]).Fitness < (nextPop[index]).Fitness)
                    {
                        index = i;
                    }
                }
                if (_elitism)
                {
                    if (!compareTour(bestTour, curPop[index], numCity))
                    {
                        curPop.RemoveAt(0);
                        curPop.Add(bestTour);
                    }
                }
            }


            public void GA_OX_VNS_applyVNSForBestTours(ref PathTour bestTour, CityObject[] coor, string outFile)
            {
                int countEvaluate = 0;
                int i = 0;
                initPathRepresetations();
                createRandomPopulation(_numCity, ref bestTour, coor, ref countEvaluate, ran);
                Evaluate eval = new Evaluate();
                _totalFitness = eval.totalFitnessPopulation(currentPathPop, _populationSize);
                ReadWriteFile writeFile = new ReadWriteFile();
                if (File.Exists(outFile))
                {
                    File.Delete(outFile);
                }
                //for (int i = 0; i < _generationSize; i++)
                writeFile.writeBestFitnessInPop(outFile, currentPathPop, _totalFitness, i + 1, bestTour);
                while (countEvaluate <= _numEvaluate)
                {
                    createNextPopulation_OX_applyVNSForBestTours(ref currentPathPop, nextPathPop, ref bestTour, _numCity, _populationSize, _touranmentSize,
                        _mutationRate, _crossoverRate, _percentVNSApplyLocalsearch, coor, ref countEvaluate, _numEvaluate, ran);
                    i++;
                    _totalFitness = eval.totalFitnessPopulation(currentPathPop, _populationSize);
                    writeFile.writeBestFitnessInPop(outFile, currentPathPop, _totalFitness, i, bestTour);

                    Console.WriteLine("      + Number of evaluation: " + countEvaluate);
                }
            }

            #endregion

            #region Thay doi su dung cac lan can cua VNS voi nhau

            //numChangeVNS -> Neu sau numChangeVNS lan danh gia ma fitness cua ca the tot nhat khong thay doi thi chuyen sang VNS khac: VNS_swapNextCity & SwapSegment + SwapInSegment
            //countEvalEqual -> Dem so lan fitness cua ca the tot nhat khong thay doi
            public void changeVNS(ref List<PathTour> curPop, int numCity, CityObject[] citiesCoor, int percentApplyLocalsearch, int numTourApplyVNS1, int numTourApplyVNS2,
                ref int countEvaluate, int totalEvaluate, ref int countEvalEqual, int numChangeVNS, int numSgm, ref PathTour btour, Random ran)
            {
                int pos = 0;
                int kmaxSwapNextCity = numCity;
                int kmaxCombine = numSgm;
                VariableNeighborHoodSearch vns = new VariableNeighborHoodSearch();
                if (percentApplyLocalsearch == 100)
                {
                    //List<PathTour> lstTemp = new List<PathTour>();
                    //for (int jj = 0; jj < _populationSize; jj++)
                    //{
                    //    if (countEvaluate > totalEvaluate)
                    //    {
                    //        break;
                    //    }
                    //    PathTour temp = new PathTour(numCity);
                    //    temp = (PathTour)(curPop[jj]).Clone();
                    //    temp = vns.VNS_swapNextCity(temp, kmax, citiesCoor, ref btour, ref countEvaluate, totalEvaluate, ran);
                    //    lstTemp.Add(temp);
                    //}
                    //curPop.Clear();
                    //for (int i = 0; i < lstTemp.Count; i++)
                    //{
                    //    curPop.Add(lstTemp[i]);
                    //}
                }
                else
                {
                    List<PathTour> tempPop1 = new List<PathTour>();
                    int numTourApplyLocalSearch = (int)(percentApplyLocalsearch * _populationSize / 100);
                    //int numTourApplyVNS1 = (int)(percentVNS1 * _populationSize / 100);
                    //int numTourApplyVNS2 = (int)(percentVNS2 * _populationSize / 100);
                    int numTourApplyVNS3 = numTourApplyLocalSearch - numTourApplyVNS1 - numTourApplyVNS2;

                    double tempBestFitness = btour.Fitness;
                    bool ok = false;

                    int jj = 0;
                    int cntNewVNS = 0;

                    while (jj < numTourApplyLocalSearch)
                    {
                        if (countEvalEqual > numChangeVNS)
                        {
                            ok = true;
                        }
                             
                        if (ok)
                        {
                            if (cntNewVNS < numTourApplyVNS2)
                            {
                                if (countEvaluate > totalEvaluate)
                                {
                                    break;
                                }
                                PathTour temp = new PathTour(numCity);
                                pos = ran.Next(_populationSize);
                                temp = (PathTour)(curPop[pos]).Clone();
                                curPop.RemoveAt(pos);
                                //temp = vns.VNS_2Opt(temp, citiesCoor, ref btour, ref countEvaluate, totalEvaluate, ran);
                                temp = vns.VNS_swapNextCity(temp, kmaxSwapNextCity, citiesCoor, ref btour, ref countEvaluate, totalEvaluate, ran);
                                curPop.Add(temp);                             
                            }
                            else
                            {
                                if (cntNewVNS < numTourApplyVNS3 + numTourApplyVNS2)
                                {
                                    if (countEvaluate > totalEvaluate)
                                    {
                                        break;
                                    }
                                    PathTour temp = new PathTour(numCity);
                                    pos = ran.Next(_populationSize);
                                    temp = (PathTour)(curPop[pos]).Clone();
                                    curPop.RemoveAt(pos);
                                    temp = vns.VNS_CombineSwapAndMoveSegment(temp, kmaxCombine, numSgm, citiesCoor, ref btour, ref countEvaluate, totalEvaluate, ran);
                                    curPop.Add(temp);                                                 
                                }
                                else
                                {
                                    if (countEvaluate > totalEvaluate)
                                    {
                                        break;
                                    }
                                    PathTour temp = new PathTour(numCity);
                                    pos = ran.Next(_populationSize);
                                    temp = (PathTour)(curPop[pos]).Clone();
                                    curPop.RemoveAt(pos);
                                    temp = vns.VNS_2Opt(temp, citiesCoor, ref btour, ref countEvaluate, totalEvaluate, ran);
                                    //temp = vns.VNS_swapNextCity(temp, kmax, citiesCoor, ref btour, ref countEvaluate, totalEvaluate, ran);
                                    curPop.Add(temp);                                           
                                }
                            }                           
                            cntNewVNS++;
                        }
                        else
                        {
                            if (countEvaluate > totalEvaluate)
                            {
                                break;
                            }
                            PathTour temp = new PathTour(numCity);
                            pos = ran.Next(_populationSize);
                            temp = (PathTour)(curPop[pos]).Clone();
                            curPop.RemoveAt(pos);
                            temp = vns.VNS_2Opt(temp, citiesCoor, ref btour, ref countEvaluate, totalEvaluate, ran);
                            //temp = vns.VNS_swapNextCity(temp, kmax, citiesCoor, ref btour, ref countEvaluate, totalEvaluate, ran);
                            curPop.Add(temp);   
                        }
                        if (tempBestFitness == btour.Fitness)
                        {
                            countEvalEqual++;
                        }
                        else
                        {
                            tempBestFitness = btour.Fitness;
                            countEvalEqual = 0;
                        }
                        if (countEvalEqual > numChangeVNS)
                        {
                            ok = true;
                            cntNewVNS = 0;
                        }
                        jj++;
                    }
                }                
            }


            public void createNextPopulation_OX_ChangeVNS(ref List<PathTour> curPop, List<PathTour> nextPop, ref PathTour bestTour, int numCity, int popSize,
                int touranmentSize, double mutationRate, double crossRate, int percentApplyLocalsearch, int numTourApplyVNS1, int numTourApplyVNS2,
                CityObject[] coor, ref int countEvaluate, int totalEvaluate, ref int countEvalEqual, int numChangeVNS, int numSgm, Random ran)
            {
                int index = 0;
                int pidx1, pidx2;
                PathTour parent1 = new PathTour(numCity);
                PathTour parent2 = new PathTour(numCity);
                PathTour child1 = new PathTour(numCity);
                PathTour child2 = new PathTour(numCity);
                nextPop.Clear();
                Selections sl = new Selections();
                for (int i = 0; i < popSize; i += 2)
                {
                    if (countEvaluate > totalEvaluate)
                    {
                        break;
                    }
                    pidx1 = sl.touranmentSelection(ran, curPop, popSize, touranmentSize);
                    pidx2 = sl.touranmentSelection(ran, curPop, popSize, touranmentSize);
                    parent1 = curPop[pidx1];
                    parent2 = curPop[pidx2];
                    if ((float)(ran.NextDouble()) < _crossoverRate)
                    {
                        Crossovers cross = new Crossovers();
                        cross.orderCrossover(parent1, parent2, out child1, out child2, ran);
                        Mutation mutation = new Mutation();
                        mutation.inversionMutation(ref child1, numCity, mutationRate, ran);
                        mutation.inversionMutation(ref child2, numCity, mutationRate, ran);

                        Evaluate eval = new Evaluate();
                        eval.calculateFitness(ref child1, ref bestTour, numCity, coor);
                        nextPop.Add(child1);
                        eval.calculateFitness(ref child2, ref bestTour, numCity, coor);
                        nextPop.Add(child2);
                        countEvaluate = countEvaluate + 2;
                    }
                    else
                    {
                        nextPop.Add(parent1);
                        nextPop.Add(parent2);
                    }
                }
                changeVNS(ref nextPop, numCity, coor, percentApplyLocalsearch, numTourApplyVNS1, numTourApplyVNS2, ref countEvaluate, totalEvaluate,
                    ref countEvalEqual, numChangeVNS, numSgm, ref bestTour, ran);

                if (countEvaluate > totalEvaluate)
                {
                    return;
                }

                curPop.Clear();
                for (int i = 0; i < _populationSize; i++)
                {
                    curPop.Add(nextPop[i]);
                    if ((nextPop[i]).Fitness < (nextPop[index]).Fitness)
                    {
                        index = i;
                    }
                }
                if (_elitism)
                {
                    if (!compareTour(bestTour, curPop[index], numCity))
                    {
                        curPop.RemoveAt(0);
                        curPop.Add(bestTour);
                    }
                }
            }

            
            public void GA_OX_ChangeVNS(ref PathTour bestTour, CityObject[] coor, string outFile, int percentAppVNS1, int percentAppVNS2, int percentChangeVNS, int numSgm)
            {
                int countEvaluate = 0;
                int countEvalEqual = 0;
                int i = 0;
                int numTourApplyVNS1 = (int)(percentAppVNS1 * _populationSize / 100);
                int numTourApplyVNS2 = (int)(percentAppVNS2 * _populationSize / 100);
                int numChangeVNS = (int)(percentChangeVNS * _populationSize / 100);

                initPathRepresetations();
                createRandomPopulation(_numCity, ref bestTour, coor, ref countEvaluate, ran);
                Evaluate eval = new Evaluate();
                _totalFitness = eval.totalFitnessPopulation(currentPathPop, _populationSize);
                ReadWriteFile writeFile = new ReadWriteFile();
                if (File.Exists(outFile))
                {
                    File.Delete(outFile);
                }
                //for (int i = 0; i < _generationSize; i++)
                writeFile.writeBestFitnessInPop(outFile, currentPathPop, _totalFitness, i + 1, bestTour);
                while (countEvaluate <= _numEvaluate)
                {
                    createNextPopulation_OX_ChangeVNS(ref currentPathPop, nextPathPop, ref bestTour, _numCity, _populationSize, _touranmentSize,
                        _mutationRate, _crossoverRate, _percentVNSApplyLocalsearch, numTourApplyVNS1, numTourApplyVNS2, coor, ref countEvaluate, _numEvaluate,
                        ref countEvalEqual, numChangeVNS, numSgm, ran);
                    i++;
                    _totalFitness = eval.totalFitnessPopulation(currentPathPop, _populationSize);
                    writeFile.writeBestFitnessInPop(outFile, currentPathPop, _totalFitness, i, bestTour);

                    Console.WriteLine("      + Number of evaluation: " + countEvaluate);
                }
            }

            
            #endregion







            #endregion




            #region Giai thuat VNS de toi uu mot tour ngau nhien ban dau
            public void VNS_Algorithm(int numCities, ref PathTour bestTour, CityObject[] citiesCoor)
        {
            int countEva = 0;
          
            PathTour ch = new PathTour(numCities);
            InitChromo initChromo = new InitChromo();
            initChromo.createPathTour(ref ch, numCities, ran);
            Evaluate eval = new Evaluate();
            eval.calculateFitness(ref ch, ref bestTour, numCities, citiesCoor);
            countEva++;

            VariableNeighborHoodSearch vnsClass = new VariableNeighborHoodSearch();
            while (countEva <= _numEvaluate)
            {
                ch = vnsClass.VNS(ch, citiesCoor, ref bestTour, ref countEva, _numEvaluate, ran);
                Console.WriteLine("      + Number of evaluation: " + countEva);
            }
        }

        public void VNS_Alg_SwapInSegment(int numCities, ref PathTour bestTour, CityObject[] citiesCoor, int numSgm)
        {
            int countEva = 0;

            PathTour ch = new PathTour(numCities);
            InitChromo initChromo = new InitChromo();
            initChromo.createPathTour(ref ch, numCities, ran);
            Evaluate eval = new Evaluate();
            eval.calculateFitness(ref ch, ref bestTour, numCities, citiesCoor);
            countEva++;

            VariableNeighborHoodSearch vnsClass = new VariableNeighborHoodSearch();
            while (countEva <= _numEvaluate)
            {
                ch = vnsClass.VNS_Alg_SwapInSegment(ch, citiesCoor, ref bestTour, numSgm, ref countEva, _numEvaluate, ran);
                Console.WriteLine("      + Number of evaluation: " + countEva);
            }
        }
        
        public void VNS_Alg_InSegment(int numCities, ref PathTour bestTour, CityObject[] citiesCoor, int numSgm)
        {
            int countEva = 0;

            PathTour ch = new PathTour(numCities);
            InitChromo initChromo = new InitChromo();
            initChromo.createPathTour(ref ch, numCities, ran);
            Evaluate eval = new Evaluate();
            eval.calculateFitness(ref ch, ref bestTour, numCities, citiesCoor);
            countEva++;

            VariableNeighborHoodSearch vnsClass = new VariableNeighborHoodSearch();
            while (countEva <= _numEvaluate)
            {
                ch = vnsClass.VNS_Alg_InSegment(ch, citiesCoor, ref bestTour, numSgm, ref countEva, _numEvaluate, ran);
                Console.WriteLine("      + Number of evaluation: " + countEva);
            }
        }
        
        public void VNS_Alg_SwapSegment(int numCities, ref PathTour bestTour, CityObject[] citiesCoor, int numSgm)
        {
            int countEva = 0;

            PathTour ch = new PathTour(numCities);
            InitChromo initChromo = new InitChromo();
            initChromo.createPathTour(ref ch, numCities, ran);
            Evaluate eval = new Evaluate();
            eval.calculateFitness(ref ch, ref bestTour, numCities, citiesCoor);
            countEva++;

            VariableNeighborHoodSearch vnsClass = new VariableNeighborHoodSearch();
            while (countEva <= _numEvaluate)
            {
                ch = vnsClass.VNS_Alg_SwapSegment(ch, citiesCoor, ref bestTour, numSgm, ref countEva, _numEvaluate, ran);
                Console.WriteLine("      + Number of evaluation: " + countEva);
            }
        }

        public void VNS_Alg_CombineSwapAndMoveSegment(int numCities, ref PathTour bestTour, CityObject[] citiesCoor, int numSgm)
        {
            int countEva = 0;

            PathTour ch = new PathTour(numCities);
            InitChromo initChromo = new InitChromo();
            initChromo.createPathTour(ref ch, numCities, ran);
            Evaluate eval = new Evaluate();
            eval.calculateFitness(ref ch, ref bestTour, numCities, citiesCoor);
            countEva++;

            VariableNeighborHoodSearch vnsClass = new VariableNeighborHoodSearch();
            while (countEva <= _numEvaluate)
            {
                ch = vnsClass.VNS_Alg_CombineSwapAndMoveSegment(ch, citiesCoor, ref bestTour, numSgm, ref countEva, _numEvaluate, ran);
                Console.WriteLine("      + Number of evaluation: " + countEva);
            }
        }



        #endregion
         

        
    }
}
